<?php

/**
 * 组装返回数据
 * @author:爱是西瓜
 * @param $type 信息类别（login,notice,msg,logout 等用于区别返回的信息用在哪里）
 * @param $data 返回数据
 * @param int $code 返回状态码
 * @param string $msg 返回状态信息
 * @return string 转换的json数据
 */
function myResult($type,$data,$code=200,$msg='请求成功'){
    $result = [
        'code'=>$code,
        'msg'=>$msg,
        'type'=>$type,
        'data'=>$data
    ];
    return json_encode($result);
}

//实例化swoole的websocket服务
$serv = new swoole_websocket_server('0.0.0.0',9507);

//监听websocket连接
$serv->on('open',function($ws,$request){

    $msg = "新用户 {$request->fd} 加入";

    $user = [
        'id' => $request->fd,
        'nickname' => '匿名用户'
    ];

    //将连接信息存放到fd的全局数组中( $request->fd 为连接id )
    $GLOBALS['fd'][$request->fd] = $user;

    //控制台 调试输出 可以删除
    echo $msg."\n";
});

//监听websocket发送上来的信息
$serv->on('message',function($ws,$request){

    //获取当前连接的用户信息
    $user = $GLOBALS['fd'][$request->fd];

    //控制台 调试输出 可以删除
    var_dump($request->data);

    //将用户发送上来的数据格式化成数组（客户端上传的json数据）
    $data = json_decode($request->data,true);

    //判断是否登录操作（模拟的用于绑定用户信息）
    if($data['a'] == 'login'){

        //组装用户信息
        $myUser = $user;
        $userInfo = $data['userInfo'];
        $myUser['token'] = $userInfo['token'];
        $myUser['nickname'] = $userInfo['nickname'];
        $myUser['avatar'] = $userInfo['avatar'];

        //将用户信息根据token存放入全局数组中
        $GLOBALS['tokens'][$myUser['token']] = $myUser;

        //控制台 调试输出 可以删除
        var_dump($myUser);

        //将用户信息根据连接id存放入全局数组中
        $GLOBALS['fd'][$request->fd] = $myUser;
        $msg = " {$myUser['nickname']} 加入聊天室 ";



        //获取当前所有用户信息
        $userlist = $GLOBALS['fd'];
        //将所有用户列表推送给客户端
        $ws->push($myUser['id'],myResult('userlist',$userlist));

        //将用户登录信息推送到所有在线客户端
        foreach ($GLOBALS['fd'] as $toUser){
            //不用推送给自己
            if($toUser['id']!=$myUser['id']) {
                $ws->push($toUser['id'], myResult('login', ['msg' => $msg, 'userInfo' => $myUser]));
            }
        }

    }


    //客户端发送信息操作
    if($data['a'] == 'msg'){

        //获取客户端信息发送给谁 0表示 所有人
        $to_uid = $data['to_uid'];
        //客户端发送的消息
        $msg = $data['msg'];

        //获取当前用户信息
        $myUser = $GLOBALS['fd'][$request->fd];
        //将信息推送给自己
        $ws->push($request->fd,myResult('msg',['msg'=>$msg,'nickname'=>$myUser['nickname'],'avatar'=>$myUser['avatar'],'add_time'=>date('Y-m-d H:i:s')]));

        //控制台 调试输出 可以删除
        echo "自己：\n";
        var_dump($myUser);

        if($to_uid){

            //控制台 调试输出 可以删除
            var_dump('token:'.$to_uid);

            //获取全中的用户信息（发送给谁的信息）
            $toUser = $GLOBALS['tokens'][$to_uid];

            //控制台 调试输出 可以删除
            var_dump('ws_uid:');
            var_dump($toUser);

            //将信息推送到指定用户客户端
            if($toUser['id']){
                $ws->push($toUser['id'],myResult('msg',['msg'=>$msg,'nickname'=>$myUser['nickname'],'avatar'=>$myUser['avatar'],'add_time'=>date('Y-m-d H:i:s')]));
            }

        }else{
            //将信息发送给所有在线客户端
            foreach ($GLOBALS['fd'] as $toUser){
                //用户列表中排除自己
                if($toUser['id']!=$myUser['id']){
                    $ws->push($toUser['id'],myResult('msg',['msg'=>$msg,'nickname'=>$myUser['nickname'],'avatar'=>$myUser['avatar'],'add_time'=>date('Y-m-d H:i:s')]));
                }
            }

        }

    }

    echo $msg."\n";

});

//监听websocket断开连接
$serv->on('close',function($ws,$ws_uid){

    $msg = "{$ws_uid} 关闭连接";

    //获取断开连接的用户信息
    $myUser = $GLOBALS['fd'][$ws_uid];
    if($myUser['token']){
        $msg = $myUser['nickname'].'退出聊天室';

        //将断开连接的用户信息推送给所有在线客户端
        foreach ($GLOBALS['fd'] as $toUser) {
            //用户列表中排除自己
            if ($toUser['id'] != $myUser['id']) {
                $ws->push($toUser['id'], myResult('logout', ['msg' => $msg, 'userInfo' => $myUser]));
            }

        }
        //将全局用户数组（token）中删除断开连接的用户
        unset($GLOBALS['tokens'][$myUser['token']]);
    }

    //将全局用户数组（连接id）中删除断开连接的用户
    unset($GLOBALS['fd'][$ws_uid]);

    //控制台 调试输出 可以删除
    echo $msg."\n";

});

//启动服务器
$serv->start();